class myQuery { 
    static Version = '1.3.0'
    static isFunction = {}
    static myFun = {
        push: [].push,
        sort: [].sort,
        splice:[].splice,
        isFunction(sel){ 
            return typeof sel === 'function'
        },
        isArray(sel){ 
            return typeof sel === 'object' && sel !== window 
        },
        isString(sel) { 
            return typeof sel === 'string'
        },
        isObject(sele) { 
            return typeof sele === 'object' && !('length' in sele)
        },
        isHTML(sel){ 
            sel = sel.trim()
            return sel[0] === '<' && sel[sel.length - 1] === '>' && sel.length >= 3
        },
        ready(fn) { 
            if (document.readyState === 'complete') {
                fn()
            } else if (document.addEventListener) { 
                document.addEventListener('DOMContentLoaded', () => { 
                    fn()
                })
            }
        },
        each(obj, callback) { 
            /*
                each方法就是用来进行遍历，原生JS的forEach方法只能够用来遍历数组，对象需要使用for in进行遍历
                    所以这里我们同意的进行each方法的封装
            */
            if (myQuery.myFun.isArray(obj)) { 
                if ('length' in obj) {
                    for (let i = 0; i < obj.length; i++) {
                        /*
                            大部分时候遍历数组或对象的时候都是为了拿到值，而不是下标火鹤键
                                所以为了方便操作，这里使用call方法，将这个function的this指向到值就行
    
                            call方法第一个参数是需要修改到this指向的对象，后面跟着的就是正常的形参
                            apply方法也可以修改this执行，第一个参数是需要修改到的对象， 第二个参数是数组，且只能传递两个参数
                            
                            在使用频率上  call 方法会比 apply 方法使用频率更加的高点
                        */
                        let res = callback.call(obj[i],i,obj[i])
                        if (res === true) {
                            continue
                        } else if (res === false) { 
                            break
                        }
                    }
                } else { 
                    for (const key in obj) {
                        let res = callback.call(obj[key],key,obj[key])
                        if (res === true) {
                            continue
                        } else if (res === false) { 
                            break
                        }
                    }
                } 
            }
        },
        getfather(sele) { 
            let opt = new myQuery(sele)[0].parentNode
            return opt
        }
    }
    constructor(selector) { 
        if (!selector) {
            return this
        } else if (myQuery.myFun.isString(selector)) {
            // 代码片段
            if (myQuery.myFun.isHTML(selector)) {
                let temp = document.createElement('div')
                temp.innerHTML = selector
                let arr = [].push.apply(this, temp.children)
            } else {
                let doms = document.querySelectorAll(selector)
                let res = [].push.apply(this, doms)
            }
        } else if (myQuery.myFun.isFunction(selector)) { 
            myQuery.myFun.ready(selector)
        }else if (myQuery.myFun.isArray(selector)) {
            if ('length' in selector) {
                for (let i = 0; i < selector.length; i++) {
                    this[i] = selector[i]
                }
                Object.defineProperties(this, 'length', {

                })
                this.length = selector.length
            } else {
                let i = 0
                for (const key in selector) {
                    i++
                    this[key] = selector[key]
                }
                this.length = i
            }
        } else if (myQuery.myFun.isHTML(selector)) { 
            console.log('hhh')
        }
        else { 
            this[0] = selector
            this.length = 1
        }
        // 设置属性特征  length属性只要能够读取就行了，并不是很想能够被遍历出来
        /*
            属性特征可以设置多种特征如下
                value //设置属性值
                writable //值可否被修改
                enumerable  //属性可否被遍历
                configurable  //可否被配置
        */ 
        Object.defineProperty(this, 'length', {
            enumerable:false
        })
        return this
    }
    toArray() {
        let arr = []
        for (const iterator in this) {
            arr.push(this[iterator])
        }
        return arr
    }
    get(num) { 
        /*
            get方法 传出指定下标的元素。
                如果未传递参数，默认将这个对象以数组的形式全部传出
                如果传递>0且小于length的数，则按照数组下标的方式传出
                如果传递<0的数，则相当于是倒着数数组的下标的形式传出数值
        */
        if (!num&&num!==0) {
            return this.toArray()
        } else if (num >= 0 && num < this.length) {
            return this[num]
        } else if (num < 0) { 
            return this[this.length + num]
        }
    }
    eq(num) { 
        /*
            获取对象中的元素，根据参数返回指定的元素
                如果不传递参数，默认返回一个空的对象
                传递>0且小于length的参数，会以访问数组的形式传递出指定下标的值
                如果传递<0的数，默认会以倒着数的方式传输指定出以后下标的值
        */ 
        if (num === undefined) {
            return new myQuery()
        } else { 
            for (const key in this) {
                if (parseInt(key) !== num) {
                    
                    delete this[key]
                }
            }
            console.log(this)
        }
        return this
    }
    first() { 
        // 获取对象中第一个元素
        return this.eq(0)
    }
    last() { 
        // 获取对象中最后一个元素
        // -1相当于是倒着数的第一个  也就是正着数的最后一个
        return this.eq(-1)
    }
    
    map(obj, callback) { 
        /*
            map方法和each方法是类似的，都是遍历数组，区别在于：
                map方法会在遍历的时候将返回值添加到一个新数组最后进行返回
                each方法则是更加注重遍历的过程，不考虑返回的结果
        */ 
        let arr = []
        if (myQuery.myFun.isArray(obj)) {
            if ('length' in obj) {
                for (let i = 0; i < obj.length; i++) {
                    let res = callback(obj[i], i)
                    if (res !== undefined) { 
                        arr.push(res)
                    }
                }
            }
        } else { 
            for (const key in obj) {
                let res = callback(obj[key], key)
                if (res !== undefined) { 
                    arr.push(res)
                }
            }
        }
        return arr
    }

    //对象方法
    empty() { 
        // 将某个对象清空。
        myQuery.myFun.each(this,(item, value) => {
            value.innerHTML = ''
        })
        return this
    }
    remove(sele) { 
        /*
            用于删除一些DOM元素，如果未传值，则表示删除的是它自身
        */
        if (sele === undefined) {
            myQuery.myFun.each(this, (item, value) => {
                // 遍历所有元素 找到一个删一个即可
                // DOM是不能够自己删除自己的 只有父元素才可以删除子元素  所以需要先找到父元素
                let parent = value.parentNode //parentNodes获取父元素
                parent.removeChild(value)  //删除指定元素
            })
        } else { 
            let $this = this
            let goal = new myQuery(sele)
            console.log('$this',$this)
            myQuery.myFun.each(goal, (item, value) => { 
                console.log('value',value)
                let parentNode = value.parentNode
                console.log('apt',parentNode)
                myQuery.myFun.each($this, (item, values) => { 
                    console.log('val',values)
                    if (values === parentNode) { 
                        values.removeChild(value)
                    }
                })
            })
        }
        // 链式编程
        return this
    }
    html(data) { 
    /*
        html方法,用于查看和修改DOM的html信息
            不传递的参数时,会返回HTML信息
            传递参数时,将HTML替换成传递的信息
    */ 
        if (data === undefined) {
            myQuery.myFun.each(this, (item, value) => {
                return value.innerHTML
            })
        } else { 
            myQuery.myFun.each(this, (item, value) => { 
                value.innerHTML = data
            })
            return this
        }
    }
    text(content) { 
        if (content === undefined) {
            let str = ''
            myQuery.myFun.each(this, (item, value) => {
                str += value.innerText
            })
            return str
        } else { 
            myQuery.myFun.each(this, (item, value) => { 
                value.innerText = content
            })
            return this
        }
    }
    appendTo(target) { 
        /*
            将一个元素添加到某个位置,默认的位置会消失
                传递一个目标元素,也就是要插入到的元素
        */ 
        let $this = this
        
        if (target !== undefined) {
            let opt = new myQuery(target)
            myQuery.myFun.each($this, (item, values) => { 
                
                myQuery.myFun.each(opt, (item, value) => { 
                    if (item === 0) {
                        value.appendChild(values)
                    } else { 
                        let demo = values.cloneNode(true)
                        value.appendChild(demo)
                    }
                })
            })
            
        }
    }
    prependTo(target) { 
        let $this = this
        if (target !== undefined) { 
            let opt = new myQuery(target)
            
            myQuery.myFun.each($this, (item, values) => { 
                console.log('5',values)
                for (const keys in opt) {
                    let first = opt[keys].childNodes[1]
                    if (keys === '0') {
                        console.log('12354')
                        opt[keys].insertBefore(values, first)
                    } else { 
                        let demo = values.cloneNode(true)
                        opt[keys].insertBefore(demo, first)
                    }
                }
            })
            return this
        }
    }
    append(sele) { 
        if (myQuery.myFun.isString(sele)) {
            for (const key in this) {
                this[key].innerHTML += sele
            }
        }
        return this
    }
    prepend(sele) { 
        if (myQuery.myFun.isString(sele)) { 
            for (const key in this) {
                this[key].innerHTML = sele + this[key].innerHTML
            }
        }
        return this
    }
    insertBefore(sele) { 
        /*
            将元素插入到指定元素外部的前面
        */
        let opt = new myQuery(sele)
        // 当需要变化位置的元素是绑定id属性时,只能就只能按照逻辑往下走
            // 当没有绑定id的时候,就应该再第一个委派排了以后之后如果有符合条件直接复制一个结点插入
        if (this[0].id) {
            for (const keys in this) {
                for (const key in opt) {
                    if (key === '0') {
                        let potfather = opt[key].parentNode
                        potfather.insertBefore(this[keys], opt[key])
                    }
                }
            }
        } else { 
            for (const keys in this) {
                for (const key in opt) {
                    let potfather = opt[key].parentNode
                    if (key === '0') {
                        potfather.insertBefore(this[keys], opt[key])
                    } else { 
                        let cloneNodes = this[0].cloneNode(true)
                        potfather.insertBefore(cloneNodes, opt[key])
                    }
                }
            }
        }
        
    }
    repalceAll(sele) { 
        /*
            将指定的代码替换到所有的区域
                先将替换的元素插入到被替换的前面,再删除原来的片段
        */
        this.insertBefore(sele)
        let opt = new myQuery(sele)
        for (const key in opt) {
            let father = opt[key].parentNode
            father.removeChild(opt[key])
        }
    }
    next() {
        // 获取当前元素同级元素的后面一个元素
            // 如果有 则返回该元素，如果没有 则返回0
        if (this[0].nextSibling.nextSibling) {
            return this[0].nextSibling.nextSibling
        } else { 
            return 0
        }
    }
    prev() { 
        // 获取相邻元素紧邻的一个同辈元素
        // 如果有，则返回该元素，如果没有则返回0
        if (this[0].previousSibling.previousSibling) {
            return this[0].previousSibling.previousSibling
        } else { 
            return 0
        }
    }
    insertAfter(sele) { 
        /*
            知识点回顾：  append 和 appendChild的区别
                append可以同时传入多个节点或者是字符串，并且是没有返回值
                appendChild只能传入一个节点，不支持字符串的传入
        */ 
        /*
            元素.insertAfter(指定元素)  ==> 将元素添加到指定元素外部的后面
                思路：判断目标元素的后面是否有相邻的兄弟元素，如果有，就将元素插入到兄弟元素的
                    前面
                    如果目标元素的后面并没有兄弟元素，说明这个元素是最后面的袁，那么只要直接插入
                    到父元素的最后面即可
        */
        let father = myQuery.myFun.getfather(sele)
        let nextdom = new myQuery(sele)[0].nextSibling.nextSibling
        if (nextdom) {
            father.insertBefore(this[0], nextdom)
        } else { 
            father.appendChild(this[0])
        }
    }
    clone(deep) { 
        /*
            克隆节点，分为深复制和浅复制，在jQuery中的浅复制就是复制整个元素的内容
                深复制时连元素的事件也会一起克隆
        */
        let clones = []
        let fathers = []
        if (!deep) {
            // 浅复制
            myQuery.myFun.each(this, (item, element) => {
                element.parentNode.appendChild(element.cloneNode(true))
            })
        } else { 
            myQuery.myFun.each(this, (item, element) => {
                let clonenode = element.cloneNode(true)
                console.log(myQuery.isFunction)
                for (const key in myQuery.isFunction) {
                    myQuery.isFunction[key].forEach((item, i) => { 
                        if (i === 0) { 
                            clonenode.addEventListener(key, item)
                        }
                    })
                }
                element.parentNode.appendChild(clonenode)
            })
        }
        
    }

    // DOM属性操作的相关方法
    attr(key,value) { 
        /* 
            功能：获取和设置DOM节点的属性 
            当传递一个参数的时候，返回一个元素的属性值
            当传递两个参数时：未所有DOM设置属性 按照传入的属性名属性值
        */
        if (myQuery.myFun.isString(key)) {
            if (arguments.length === 1) {
                return this[0].getAttribute(key)
            } else {
                myQuery.myFun.each(this, (item, dom) => {
                    dom.setAttribute(key, value)
                })
            }
        } else { 
            let $this = this
            if (myQuery.myFun.isObject(key)) { 
                myQuery.myFun.each(key, (items, values) => { 
                    myQuery.myFun.each($this, (item, value) => { 
                        value.setAttribute(items,values)
                    })
                })
            }
        }
        return this
    }
    prop(key, value) {
        /*
            prop是操作属性的 功能和attr类似  attr是操作属性节点的也就是DOM节点
                而prop是直接操作属性的  --  可以直接通过[]的方式来直接操作属性
        */
        if (myQuery.myFun.isString(key)) {
            if (arguments.length === 1) {
                return this[0][key]
            } else {
                myQuery.myFun.each(this, (item, dom) => {
                    dom[key] = value
                })
            }
        } else { 
            let $this = this
            if (myQuery.myFun.isObject(key)) { 
                myQuery.myFun.each(key, (items, values) => { 
                    myQuery.myFun.each($this, (item, value) => { 
                        value[items] = values
                    })
                })
            }
        }
        return this
    }
    css(key, value) { 
        /* 获取或者设置DOM的CSS样式信息 */ 
        if (myQuery.myFun.isString(key)) {
            if (arguments.length === 1) {
                console.log(key)
                console.log(this[0])
                return window.getComputedStyle(this[0])[key]
            } else {
                myQuery.myFun.each(this, (item, dom) => {
                    dom.style[key] = value
                })
            }
        } else { 
            let $this = this
            if (myQuery.myFun.isObject(key)) { 
                myQuery.myFun.each(key, (items, values) => { 
                    myQuery.myFun.each($this, (item, value) => { 
                        value.style[items] = values
                    })
                })
            }
        }
        return this
    
    }
    val(content) { 
        /*
            val方法用于获取当前元素的value值
                不传递参数时，默认返回的第一个对象的value
                传递参数时，会将所有这个类型的对象的value值设置成传递的参数
        */ 
        if (arguments.length === 0) {
            return this[0].value
        } else { 
            myQuery.myFun.each(this, (item, element) => { 
                element.value = content
            })
        }
    }
    hasClass(name) { 
        /*
            判断对象中是否含有指定的类名
                如果多个对象中有一个含有则返回true  
                不传递参数直接返回false
        */
        if (arguments.length === 0) {
            return false
        } else { 
            let i = 0
            myQuery.myFun.each(this, (item, element)=>{ 
                let arr = element.className.split(' ')
                if (arr.includes(name)) { 
                    i++
                }
            })
            if (i > 0) {
                return true
            } else { 
                return false
            }
        }
    }
    addClass(classNames) { 
        /*
            为当前选中的元素添加一个类，如果当前的元素已经有这个类的，就不添加
            如果为传递参数，则默认返回自己
            如果传递了参数，就修改之后的对象结果返回
        */
        if (arguments.length === 0) {
            return this
        } else { 
            let classlist = classNames.split(' ')
            myQuery.myFun.each(classlist, (item, cname) => { 
                myQuery.myFun.each(this, (item, element) => {
                    if (element.className.split(' ').includes(cname)) {
                        return
                    } else { 
                        element.classList.add(cname)
                    }
                })
            })
            return this
        }
        
    }
    removeClass(classNames) { 
        /*
            删除当前选中元素的类中指定的类
                如果有这个类，则删除合格类，如果没有，
                如果没有，则不删除
                如果没有传递参数，则默认是删除所有的类
        */
        if (arguments.length === 0) {
            myQuery.myFun.each(this, (item, element) => {
                element.className = ''
            })
        } else { 
            let cnames = classNames.split(' ')
            myQuery.myFun.each(cnames, (item, cname) => { 
                myQuery.myFun.each(this, (item, element) => { 
                    if (element.className.split(' ').includes(cname)) {
                        element.classList.remove(cname)
                    }
                })
            })
        }
        return this
    }
    toggleClass(classNames) { 
        /*
            判断当前的元素有没有传递的这个类， 
                如果有，则删除，如果没有，则添加
                如果不传参数，则默认是删除所有的类
        */
        if (arguments.length === 0) {
            myQuery.myFun.each(this, (item, element) => {
                element.className = ''
            })
        } else { 
            let cnames = classNames.split(' ')
            myQuery.myFun.each(cnames, (item, cname) => { 
                myQuery.myFun.each(this, (item, element) => { 
                    if (element.className.split(' ').includes(cname)) {
                        element.classList.remove(cname)
                    } else { 
                        element.classList.add(cname)
                    }
                })
            })
        }
        return this
    }
    // 事件的方法
    on(ename, callback) { 
        // 用于监听事件的方法，高级浏览器可以使用addEventListener  第六浏览器不支持这个方法，需要
            // 使用 attachEvent
        /*
            on方法的特点：一个元素可以注册多个相同的事件，并且是不会覆盖之前的事件
            IE8及其以下的浏览器当一个元素有多个相同的事件时，会倒序的输出事件
        */
        if (Object.keys(myQuery.isFunction).length === 0) {
            myQuery.isFunction[ename] = []
            myQuery.isFunction[ename].push(callback)
        } else { 
            for (const key in myQuery.isFunction) {
                if (key === ename) {
                    myQuery.isFunction[ename].push(callback)
                } else { 
                    myQuery.isFunction[ename] = []
                    myQuery.isFunction[ename].push(callback)
                }
            }
        }
        for (const key in this) {
            if (this[key].addEventListener) {
                this[key].addEventListener(ename, () => { 
                    for (const key in myQuery.isFunction) {
                        if (key === ename && myQuery.isFunction[key].includes(callback)) { 
                            callback()
                        }
                    }
                })
            } else { 
                // // attachEvent是为了适配IE8及其以下的浏览器的，在事件名前必须加上on才会生效
                // this[key].attachEvent(`onclick`,callback)
            }
        }
    }
    off(ename, callback) { 
        /*
            事件注销方法，用来注销选中元素的事件
                当不传递参数的时候，默认清楚全部的事件
                传递一个参数的时候，会清楚一个传递的事件的所有方法
                当传递两个参数的时候，会注销指定事件的指定方法
        */ 
        if (arguments.length === 0) {
            myQuery.isFunction = {}
        } else if (arguments.length === 1) {
            myQuery.isFunction[ename] = []
        } else if (arguments.length === 2) { 
            myQuery.myFun.each(myQuery.isFunction, (item, funs) => { 
                console.log(funs)
                if (ename === item) { 
                    for (const key in funs) {
                        if (funs[key].toString() === callback.toString()) { 
                            // splice才是删除数组时用到的方法 
                            // slice是截取时用到的方法
                            funs.splice(funs[key],1)
                        }
                    }
                }
            })
        }
    }
}
